package tr.edu.metu.is.mega.client.core;

import java.util.ArrayList;
import java.util.List;

import tr.edu.metu.is.mega.common.dto.BaseDTO;

import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;

/**
 * Abstract class for all single selection dialogs.
 * 
 * @param <T> type modifier
 */
public abstract class SelectionDialog<T extends BaseDTO> extends Window {

    private DialogCallback<T> callback;

    private Button buttonOK;

    private Button buttonCancel;

    private final boolean multiSelect;

    private boolean editEnabled;

    /**
     * Default constructor.
     * 
     * @param multiSelect multiple select mode indicator
     * @param editEnabled edit mode indicator
     */
    public SelectionDialog(boolean multiSelect, boolean editEnabled) {
        this(multiSelect, editEnabled, (Object[]) null);
    }

    /**
     * Alternative constructor. This constructor calls
     * "beforeInitSelectionWidget" procedure before call "getSelectionWidget"
     * procedure. Use this constructor if your constructor takes argument and
     * use this argument in "getSelectionWidget" procedure
     * 
     * @param multiSelect multi select mode
     * @param editEnabled edit mode indicator
     * @param initializationParameters initialization parameters of dialog (to
     *        be delegated to concrete subclasses
     */
    public SelectionDialog(boolean multiSelect, boolean editEnabled, Object... initializationParameters) {
        this.multiSelect = multiSelect;
        this.editEnabled = editEnabled;
        setModal(true);

        this.setLayout(new BorderLayout());
        LayoutContainer selectionWidget = getSelectionWidget(initializationParameters);
        BorderLayoutData layoutData = new BorderLayoutData(LayoutRegion.CENTER);
        this.add(selectionWidget, layoutData);

        buttonOK = new Button("Select");
        buttonOK.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                doSelect();
            }
        });
        addButton(buttonOK);

        buttonCancel = new Button("Cancel");
        buttonCancel.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                doCancel();
            }
        });
        addButton(buttonCancel);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void afterRender() {
        super.afterRender();
        center();
    }

    /**
     * Select action handler.
     */
    protected void doSelect() {
        List<T> selectedItems = getSelectedItems();
        hide();
        if (selectedItems.size() == 0) {
            MessageBox.info("Hata", "Kayıt seçili değil.", null);
            return;
        }

        if (multiSelect) {
            callback.onOk(selectedItems);
        } else {
            callback.onOk(selectedItems.get(0));
        }
    }

    /**
     * Cancel action handler.
     */
    private void doCancel() {
        hide();
        callback.onCancel();
    }

    /**
     * Displays this for selection of an object.
     * 
     * @param dialogCallback dialog callback
     * @param selectedItem pre-selected object
     */
    public final void select(DialogCallback<T> dialogCallback, T selectedItem) {
        this.callback = dialogCallback;

        if (selectedItem != null) {
            List<T> list = new ArrayList<T>();
            list.add(selectedItem);
            setSelectedItems(list);
        }
        show();
        center();
    }

    /**
     * Displays this for selection of an object.
     * 
     * @param dialogCallback dialog callback
     * @param selectedItems list of selected objects
     */
    public final void select(DialogCallback<T> dialogCallback, List<T> selectedItems) {
        this.callback = dialogCallback;

        setSelectedItems(selectedItems);
        show();
        center();
    }

    /**
     * Displays this for selection of an object.
     * 
     * @param dialogCallback dialog callback
     */
    public final void select(DialogCallback<T> dialogCallback) {
        select(dialogCallback, (T) null);
    }

    /**
     * Returns true when selection mode is multiple.
     * 
     * @return true when selection mode is multiple
     */
    protected final boolean isMultiSelect() {
        return multiSelect;
    }

    /**
     * Returns true when edit mode is enabled.
     * 
     * @return true when edit mode is enabled
     */
    protected final boolean isEditEnabled() {
        return editEnabled;
    }

    /**
     * Returns selected object.
     * 
     * @return selected object
     */
    protected abstract List<T> getSelectedItems();

    /**
     * Sets selected object.
     * 
     * @param selectedItems the selected objects
     */
    protected abstract void setSelectedItems(List<T> selectedItems);

    /**
     * Returns selection widget.
     * 
     * @param parameters initialization parameters
     * @return selection widget
     */
    protected abstract LayoutContainer getSelectionWidget(Object... parameters);

}
